#ifndef LONGKEY_PCAGENT_LKPCAGENT_H_
#define LONGKEY_PCAGENT_LKPCAGENT_H_

#include <atlbase.h>
#include <atlcom.h>
#include <atlstr.h>
#include <vector>

#include "pcagent_idl.h"
#include "../base/basictypes.h"
#include "../base/atlregmapex.h"
#include "../base/constants.h"
#include "non_localized_resource.h"
#include "com_proxy.h"

// The ATL Singleton Class Factory does not work very well if errors happen in
// CreateInstance(), the server continues running. This is because the module
// count is not incremented or decremented. This class fixes the issue so that
// on error, the server shuts down as expected.
template <class T>
class SingletonClassFactory : public CComClassFactorySingleton<T>
{
public:
	SingletonClassFactory() {}
	virtual ~SingletonClassFactory() {}

	STDMETHOD(CreateInstance)(LPUNKNOWN unk, REFIID iid, void ** obj)
	{
		HRESULT hr = CComClassFactorySingleton<T>::CreateInstance(unk, iid, obj);
		if (FAILED(hr))
		{
			CORE_LOG(LE, (_T("[SingletonClassFactory::CreateInstance failed][0x%x]")
				_T("[pulsing module count]"), hr));
			LockServer(TRUE);
			LockServer(FALSE);
		}
		return hr;
	}

private:
	DISALLOW_COPY_AND_ASSIGN(SingletonClassFactory);
};


template <bool machine, const TCHAR* const progid, const GUID& clsid,
		  UINT registry_resid, const TCHAR* const hkroot>
struct LKPCAgentCOMClassMode
{
	static bool is_machine() { return machine; }
	static const TCHAR* const prog_id() { return progid; }
	static GUID class_id() { return clsid; }
	static UINT registry_res_id() { return registry_resid; }
	static const TCHAR* const hk_root() { return hkroot; }
};

#pragma warning(push)
// C4640: construction of local static object is not thread-safe
#pragma warning(disable : 4640)
// C4505: unreferenced IUnknown local functions have been removed
#pragma warning(disable : 4505)

template <typename T>
class ATL_NO_VTABLE LKPCAgentCOMClass
	: public CComObjectRootEx<CComObjectThreadModel>,
	  public CComCoClass<LKPCAgentCOMClass<T>>,
	  public IDispatchImpl<ILKPCAgent, &__uuidof(ILKPCAgent), &CAtlModule::m_libid, kMajorTypeLibVersion, kMinorTypeLibVersion>,
	  public StdMarshalInfo
{
public:
	typedef LKPCAgentCOMClass<T> LKPCAgentCOMClassT;
	typedef SingletonClassFactory<LKPCAgentCOMClassT> SingletonClassFactoryT;

	LKPCAgentCOMClass() : StdMarshalInfo(T::is_machine()) {}
	virtual ~LKPCAgentCOMClass() {}

	DECLARE_CLASSFACTORY_EX(SingletonClassFactoryT)
	DECLARE_NOT_AGGREGATABLE(LKPCAgentCOMClassT)
	DECLARE_PROTECT_FINAL_CONSTRUCT()

	DECLARE_REGISTRY_RESOURCEID_EX(T::registry_res_id())

	BEGIN_REGISTRY_MAP()
		REGMAP_ENTRY(_T("HKROOT"), T::hk_root())
		REGMAP_EXE_MODULE(_T("MODULE"))
		REGMAP_ENTRY(_T("VERSION"), _T("1.0"))
		REGMAP_ENTRY(_T("PROGID"), T::prog_id())
		REGMAP_ENTRY(_T("DESCRIPTION"), _T("LKPCAgentCOMClass"))
		REGMAP_ENTRY(_T("CLSID"), T::class_id())
	END_REGISTRY_MAP()

	BEGIN_COM_MAP(LKPCAgentCOMClassT)
		COM_INTERFACE_ENTRY(ILKPCAgent)
		COM_INTERFACE_ENTRY(IDispatch)
		COM_INTERFACE_ENTRY(IStdMarshalInfo)
	END_COM_MAP()

	STDMETHODIMP SetUserNameAndPassword(BSTR szUsername, BSTR szPassword)
	{
		return E_NOTIMPL;
	}

	HRESULT FinalConstruct()
	{
		CORE_LOG(L2, (_T("[LKPCAgentCOMClass::FinalConstruct]")));

// 		HRESULT hr = InitializeWorker();
// 		if (FAILED(hr))
// 		{
// 			CORE_LOG(LE, (_T("[InitializeWorker failed][0x%x]"), hr));
// 			return hr;
// 		}
// 
// 		interlocked_exchange_pointer(&model_, Worker::Instance().model());
// 		ASSERT1(model());

		return S_OK;
	}

	void FinalRelease()
	{
		CORE_LOG(L2, (_T("[InitializeWorker::FinalRelease]")));
	}

private:
	DISALLOW_COPY_AND_ASSIGN(LKPCAgentCOMClass);
};

#pragma warning(pop)

extern TCHAR kHKRootUser[];
extern TCHAR kHKRootMachine[];
extern TCHAR kHKRootService[];
extern TCHAR kProgIDLKPCAgentCOMClassUserLocal[];
extern TCHAR kProgIDLKPCAgentCOMClassMachineLocal[];
extern TCHAR kProgIDLKPCAgentCOMClassServiceLocal[];

typedef LKPCAgentCOMClassMode<false, 
			kProgIDLKPCAgentCOMClassUserLocal,
			__uuidof(LongkeyPCAgentUserClass),
			IDR_LOCAL_SERVER_RGS,
			kHKRootUser> LKPCAgentCOMClassModeUser;

typedef LKPCAgentCOMClass<LKPCAgentCOMClassModeUser> LKPCAgentCOMClassUser;

#endif	// LONGKEY_PCAGENT_LKPCAGENT_H_